using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using EvoGame.World;
using EvoGame.GameObject;

namespace EvoGame
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public static GraphicsDevice device;

        static int width = 1024;
        static int height = width / 16 * 10;

        static float playerSpeed = 0;

        Vector2 playerPos = Vector2.Zero;

        SpriteSheet sheet32;
        Camera2D camera;
        LayeredTileMap Layermap;
        Tile[] tile_StoneBrick;
        Sprite player;
        Map map;

        Rectangle visibleArea;

        KeyboardState old;

        public static List<Tile> tiles = new List<Tile>();

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        int xCount, yCount;

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            InitGraphicsMode(width, height, false);
            camera = new Camera2D(GraphicsDevice.Viewport.Bounds);

            visibleArea = GraphicsDevice.Viewport.Bounds;

            sheet32 = new SpriteSheet("Images/Blocks", 34);

            Layermap = new LayeredTileMap();
            map = new Map();

            tile_StoneBrick = new Tile[52 * 52];

            //this.IsMouseVisible = true;

            device = graphics.GraphicsDevice;

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            sheet32.load(Content);

            Layermap.addMap(map);

            for (int y = 2; y < 52; y++)
            {
                for (int x = 2; x < 52; x++)
                {
                    tile_StoneBrick[x + y * 52] = new Tile(1, "StoneBrick", sheet32.Crop(new Rectangle(0, 0, 34, 34)), new Vector2(x, y));

                    tile_StoneBrick[x + y * 52].IsSolid = true;

                    Layermap.addTileToMap(map, tile_StoneBrick[x + y * 52]);
                }
            }

            player = new PlayerSprite(playerPos, sheet32.Crop(new Rectangle(0, 0, 34, 34)));
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {

            playerSpeed = 0.25f * gameTime.ElapsedGameTime.Milliseconds;
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            bool block = false;

            Vector2 oldp = playerPos;

            Vector2 DPlayerPos = new Vector2();

            int x = (int)(playerPos.X / 17 + 1.9);
            int y = (int)(playerPos.Y / 17 + 1.9);

            if (Layermap.getTile(map, x, y) != null)
            {
                Tile t = Layermap.getTile(map, y, x);
                if (!Intersects(player.Bounds, t.BoundingBox))
                {
                    if (((Keyboard.GetState().IsKeyDown(Keys.S) != old.IsKeyDown(Keys.W)) || (Keyboard.GetState().IsKeyDown(Keys.A) != old.IsKeyDown(Keys.D))) || ((Keyboard.GetState().IsKeyDown(Keys.W) != old.IsKeyDown(Keys.S)) || (Keyboard.GetState().IsKeyDown(Keys.D) != old.IsKeyDown(Keys.A))))
                    {
                        block = true;
                    }
                }
            }
            if (!block)
            {
                Move();
            }
            block = false;
            old = Keyboard.GetState();
            base.Update(gameTime);
        }

        public void Move()
        {
            if (Keyboard.GetState().IsKeyDown(Keys.W))
            {
                camera.Pos = new Vector2(camera.Pos.X, camera.Pos.Y - playerSpeed);
                playerPos = new Vector2(playerPos.X, playerPos.Y - playerSpeed);
            }
            if (Keyboard.GetState().IsKeyDown(Keys.S))
            {
                camera.Pos = new Vector2(camera.Pos.X, camera.Pos.Y + playerSpeed);
                playerPos = new Vector2(playerPos.X, playerPos.Y + playerSpeed);
            }
            if (Keyboard.GetState().IsKeyDown(Keys.D))
            {
                camera.Pos = new Vector2(camera.Pos.X + playerSpeed, camera.Pos.Y);
                playerPos = new Vector2(playerPos.X + playerSpeed, playerPos.Y);
            }
            if (Keyboard.GetState().IsKeyDown(Keys.A))
            {
                camera.Pos = new Vector2(camera.Pos.X - playerSpeed, camera.Pos.Y);
                playerPos = new Vector2(playerPos.X - playerSpeed, playerPos.Y);
            }
        }

        protected bool Intersects(Rectangle rectA, Rectangle rectB)
        {
            // Returns True if rectA and rectB contain any overlapping points
            return (rectA.Right > rectB.Left && rectA.Left < rectB.Right &&
                    rectA.Bottom > rectB.Top && rectA.Top < rectB.Bottom);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, camera.viewMatrix);
            foreach (Tile tile in Layermap.getMap(1).map)
            {
                if (!visibleArea.Contains(new Rectangle(Convert.ToInt32(camera.viewMatrix.Translation.X), Convert.ToInt32(camera.viewMatrix.Translation.Y), 1024, 1024 / 16 * 10)))
                {
                    spriteBatch.Draw(tile.texture, new Vector2(tile.pos.X * 17, tile.pos.Y * 17), null, Color.White, 0f, new Vector2(17), 1f, SpriteEffects.None, 0);  
                }
            }
            sheet32.render(spriteBatch, playerPos, new Vector2(17), 0, 0, 1f, 0f, 0);
            spriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// Attempt to set the display mode to the desired resolution.  Itterates through the display
        /// capabilities of the default graphics adapter to determine if the graphics adapter supports the
        /// requested resolution.  If so, the resolution is set and the function returns true.  If not,
        /// no change is made and the function returns false.
        /// </summary>
        /// <param name="iWidth">Desired screen width.</param>
        /// <param name="iHeight">Desired screen height.</param>
        /// <param name="bFullScreen">True if you wish to go to Full Screen, false for Windowed Mode.</param>
        private bool InitGraphicsMode(int iWidth, int iHeight, bool bFullScreen)
        {
            // If we aren't using a full screen mode, the height and width of the window can
            // be set to anything equal to or smaller than the actual screen size.
            if (bFullScreen == false)
            {
                if ((iWidth <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width)
                    && (iHeight <= GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height))
                {
                    graphics.PreferredBackBufferWidth = iWidth;
                    graphics.PreferredBackBufferHeight = iHeight;
                    graphics.IsFullScreen = bFullScreen;
                    graphics.ApplyChanges();
                    return true;
                }
            } else
            {
                // If we are using full screen mode, we should check to make sure that the display
                // adapter can handle the video mode we are trying to set.  To do this, we will
                // iterate thorugh the display modes supported by the adapter and check them against
                // the mode we want to set.
                foreach (DisplayMode dm in GraphicsAdapter.DefaultAdapter.SupportedDisplayModes)
                {
                    // Check the width and height of each mode against the passed values
                    if ((dm.Width == iWidth) && (dm.Height == iHeight))
                    {
                        // The mode is supported, so set the buffer formats, apply changes and return
                        graphics.PreferredBackBufferWidth = iWidth;
                        graphics.PreferredBackBufferHeight = iHeight;
                        graphics.IsFullScreen = bFullScreen;
                        graphics.ApplyChanges();
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
